//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//


#include <iostream>
#include <string>
#include <exception>
#include <stdexcept>
#include <vector>
#include <iomanip>
#include <sstream>
#include <fstream>

#include <cassert>
#include <cstdlib>

#include "boost/lexical_cast.hpp"

#include "cpp-lib/network.h"
#include "cpp-lib/util.h"


using namespace cpl::util::network ;
using namespace cpl::util          ;


namespace {

void usage( std::string const prog ) {

  std::cout 
    << "usage:\n" 
    << prog << " host <destination port> <source port>\n" 
    << prog << " receive <port>\n" 
    << prog << " receive-file <port>\n" 
  ;

}

//
// Return a zero padded string representing floor( x ).
//

std::string const zero_padded_string
( double const& x , unsigned const d ) {

  std::ostringstream os ;

  os << std::fixed
     << std::setw( d )
     << std::right
     << std::setfill( '0' )
     << std::setprecision( 0 )
     << x
  ;

  return os.str() ;

}

// Check datagram sender/receiver on same port (should fail).
void same_port() {

  datagram_sender s( "4711" ) ;

  try { 
  
    datagram_receiver r( "4711" ) ; 
    std::cerr 
      << "Created datagram sender and receiver on same port.\n"
      << "This should fail with the current implementation.\n"
    ;

    std::exit( 1 ) ;
    
  } catch( std::exception const& e ) {}

}


} // anonymous namespace


int main( int argc , char const* const* const argv ) {

  try {
  
  same_port() ;
  
  if( argc < 2 ) 
  { usage( argv[ 0 ] ) ; return 1 ; }

  if(    std::string( "receive"      ) == argv[ 1 ]
      || std::string( "receive-file" ) == argv[ 1 ]
  ) {

    if( argc != 3 )
    { usage( argv[ 0 ] ) ; return 1 ; }

    datagram_receiver r( argv[ 2 ] ) ;

    std::cerr << "Receiving packets on: " << r.local() << std::endl ;

    long n = 0 ;
    bool const to_file = std::string( "receive-file" ) == argv[ 1 ] ;
    
    while( true ) {

      datagram_receiver::size_type size ;

      if( !to_file ) {

        std::cerr << "Data: " << std::flush ;
        size = r.receive( std::ostreambuf_iterator< char >( std::cout ) ) ;
        std::cout << std::flush ;
        std::cerr << std::endl ;

      } else {

        std::ofstream os( 
          ( "udp-receive-" + zero_padded_string( n , 8 ) ).c_str() 
        ) ;

        always_assert( os ) ;

        size = r.receive( std::ostreambuf_iterator< char >( os ) ) ;

        always_assert( os ) ;

      }
      
      ++n ;
      
      std::cerr 
        << "Received packet #" 
        << n 
        << ": " 
        << size
        << " bytes, source: "
        << r.source()
        << std::endl 
      ;

    }

  } else {

    // Send!

    if( argc != 4 ) 
    { usage( argv[ 0 ] ) ; return 1 ; }

    {
      std::string const hello = "Hi there!" ;

      datagram_sender().send
      ( argv[ 1 ] , argv[ 2 ] , hello.begin() , hello.end() ) ;
    }

    datagram_address const dest = 
      resolve_datagram( argv[ 1 ] , argv[ 2 ] ).front() ;
    
    datagram_sender s( argv[ 3 ] ) ;

    std::cerr << "Sending from: " << s.local() << std::endl ;
    std::cerr << "Destination: " << dest << std::endl ;

    std::string ss ;
    while( std::getline( std::cin , ss ) )
    { s.send( dest , ss.begin() , ss.end() ) ; }

  }


  } // end global try
  catch( std::exception const& e ) {

    std::cerr << e.what() << std::endl ;
    return 1 ;

  }

}
